package com.daervin.cpe.utils;

import com.daervin.cpe.constants.Language;
import com.daervin.cpe.dto.ExtendData;
import com.daervin.cpe.dto.ExtendGroupData;
import com.daervin.cpe.dto.ResultData;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author daervin
 * @date 2019/11/19
 */
public final class TranslateUtil {
    private TranslateUtil() {
    }

    private static final String URL = "https://translate.google.cn/translate_a/single";
    private static final List<String> DTS = Arrays.asList("at", "bd", "ex", "ld", "md", "qca", "rw", "rm", "ss", "t");
    private static Map<String, String> FORM_DATA = new HashMap<>();

    private static final String JS_SCRIPT = "function token(a) {\n" +
            "    var k = \"\";\n" +
            "    var b = 406644;\n" +
            "    var b1 = 3293161072;\n" +
            "\n" +
            "    var jd = \".\";\n" +
            "    var sb = \"+-a^+6\";\n" +
            "    var Zb = \"+-3^+b+-f\";\n" +
            "\n" +
            "    for (var e = [], f = 0, g = 0; g < a.length; g++) {\n" +
            "        var m = a.charCodeAt(g);\n" +
            "        128 > m ? e[f++] = m: (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023), e[f++] = m >> 18 | 240, e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224, e[f++] = m >> 6 & 63 | 128), e[f++] = m & 63 | 128)\n" +
            "    }\n" +
            "    a = b;\n" +
            "    for (f = 0; f < e.length; f++) a += e[f],\n" +
            "    a = RL(a, sb);\n" +
            "    a = RL(a, Zb);\n" +
            "    a ^= b1 || 0;\n" +
            "    0 > a && (a = (a & 2147483647) + 2147483648);\n" +
            "    a %= 1E6;\n" +
            "    return a.toString() + jd + (a ^ b)\n" +
            "};\n" +
            "\n" +
            "function RL(a, b) {\n" +
            "    var t = \"a\";\n" +
            "    var Yb = \"+\";\n" +
            "    for (var c = 0; c < b.length - 2; c += 3) {\n" +
            "        var d = b.charAt(c + 2),\n" +
            "        d = d >= t ? d.charCodeAt(0) - 87 : Number(d),\n" +
            "        d = b.charAt(c + 1) == Yb ? a >>> d: a << d;\n" +
            "        a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d\n" +
            "    }\n" +
            "    return a\n" +
            "}";

    static {
        FORM_DATA.put("client", "webapp");
        FORM_DATA.put("hl", "zh-CN");
        FORM_DATA.put("ssel", "0");
        FORM_DATA.put("tsel", "3");
        FORM_DATA.put("kc", "0");
    }

    public static ResultData execute(Language from, Language to, String words) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        FORM_DATA.put("sl", from.getLan());
        FORM_DATA.put("tl", to.getLan());
        FORM_DATA.put("tk", token(words));
        FORM_DATA.put("q", words);
        URIBuilder uri = new URIBuilder(URL);
        for (String key : FORM_DATA.keySet()) {
            String value = FORM_DATA.get(key);
            uri.addParameter(key, value);
        }
        for (String dt : DTS) {
            uri.addParameter("dt", dt);
        }
        HttpUriRequest request = new HttpGet(uri.toString());
        CloseableHttpResponse response = httpClient.execute(request);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "utf-8");
        EntityUtils.consume(entity);
        response.getEntity().getContent().close();
        response.close();
        if (StringUtils.isEmpty(result)) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = mapper.readTree(result);
        JsonNode jsonNode0 = jsonNode.get(0);
        ResultData resultData = new ResultData();
        if (jsonNode0 != null) {
            resultData.setTranslation(jsonNode0.get(0).get(0).asText());
            resultData.setOriginal(jsonNode0.get(0).get(1).asText());
            if (jsonNode0.get(1).get(3) != null) {
                resultData.setOriginalPinyin(jsonNode0.get(1).get(3).asText());
            } else if (jsonNode0.get(1).get(2) != null) {
                resultData.setOriginalPinyin(jsonNode0.get(1).get(2).asText());
            }
        }
        JsonNode jsonNode1 = jsonNode.get(1);
        if (jsonNode1 != null && jsonNode1.size() > 0) {
            List<ExtendGroupData> extendMap = new ArrayList<>();
            for (int i = 0; i < jsonNode1.size(); i++) {
                JsonNode jsonNodeSub = jsonNode1.get(i);
                if (jsonNodeSub == null) {
                    continue;
                }
                List<ExtendData> extendData = new ArrayList<>();
                String part = jsonNodeSub.get(0).asText().toString();
                List<String> enList = new ArrayList<>();
                for (int j = 0; j < jsonNodeSub.get(1).size(); j++) {
                    String en = jsonNodeSub.get(1).get(j).asText();
                    enList.add(en);
                }
                for (int k = 0; k < jsonNodeSub.get(2).size(); k++) {
                    JsonNode tmp = jsonNodeSub.get(2).get(k);
                    if (tmp.size() < 3) {
                        continue;
                    }
                    String translationSub = tmp.get(0).asText();
                    if (!enList.contains(translationSub)) {
                        continue;
                    }
                    String rateSub = tmp.get(3) == null ? null : tmp.get(3).asText();
                    StringBuilder chineseSub = new StringBuilder();
                    JsonNode chineseNode = tmp.get(1);
                    if (chineseNode.size() > 0) {
                        for (int c = 0; c < chineseNode.size(); c++) {
                            chineseSub.append(chineseNode.get(c).asText()).append(",");
                        }
                    }
                    ExtendData extendDataItem = new ExtendData();
                    extendDataItem.setChinese(chineseSub.length() > 1 ? chineseSub.substring(0, chineseSub.length() - 1) : chineseSub.toString());
                    extendDataItem.setRate(rateSub == null ? BigDecimal.ZERO : new BigDecimal(rateSub));
                    extendDataItem.setTranslation(translationSub);
                    extendData.add(extendDataItem);
                }
                ExtendGroupData groupData = new ExtendGroupData();
                groupData.setPart(part);
                groupData.setData(extendData);
                extendMap.add(groupData);
            }
            resultData.setExtendData(extendMap);
        }
        return resultData;
    }

    private static String token(String text) {
        String tk = "";
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        try {
//            FileReader fileReader = new FileReader("./script/Google.js_script");
//            engine.eval(fileReader);
            engine.eval(JS_SCRIPT);

            if (engine instanceof Invocable) {
                Invocable invoke = (Invocable) engine;
                tk = String.valueOf(invoke.invokeFunction("token", text));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tk;
    }
}
